#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netdb.h>
#include <netinet/ether.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>
#include <syslog.h>

extern "C" {
#include <pcap.h>
}

#include <cstdio>
#include <cmath>
#include <iostream>
#include <memory>
#include <sstream>
#include <list>
#include <map>


#include "ip.h"
#include "prio.h"
#include "dns.h"
#include "sqldata.h"
#include "monlist.h"

using namespace odbc;
using namespace std;


// odbc ISO YYYY-MM-DD HH24:MI:SS.FFF
//
static void timevalToDate(const struct timeval *in, char *out)
{
   struct tm *ti = localtime(&in->tv_sec);
	sprintf(out, "%04d-%02d-%02d %02d:%02d:%02d.%d", 
			ti->tm_year + 1900, ti->tm_mon + 1, ti->tm_mday,
			ti->tm_hour, ti->tm_min, ti->tm_sec,
			in->tv_usec);
}

void	SQLData::InitPreparedStatements()
{
	iicStmt_ = con_->prepareStatement(
			"INSERT INTO connections (src_ip_serial, dst_ip_serial, "
				"port_src, port_dst, "
				"protocol_number, "
				"first_seen, last_seen, packets, "
				"volume, interface, flushed_at, captured_data) "
					"VALUES (?, ?,"		// 1 2
							"?, ?,"			// 3 4
							"?,"				// 5 
							"?, ?, ?,"		// 6 7 8 
							"?, ?, ?, ?)");	// 9 10 11 12
	sdnsStmt_ = con_->prepareStatement("SELECT serial "
										"FROM dnsentry "
										"WHERE hostname = ? AND ip = ?");
	pinsertStmt_ = con_->prepareStatement("INSERT INTO dnsentry  (hostname, ip, mac) "
										"VALUES (?, ?, ?)");
}

bool	SQLData::SaveRecord(CRecord *cr, string interfaceName,  struct timeval *now)
{
	if (connected_ == false)
		if (!Connect()) 
			return false;

//	pthread_mutex_lock(&SqlWriteMutex_);
	//f_volume = cr->volume_m;
	//f_packets = cr->packets_m;
	iicStmt_->setInt(1, cr->srcSerial_m); iicStmt_->setInt(2, cr->dstSerial_m);
	iicStmt_->setInt(3, cr->port_src_m); iicStmt_->setInt(4, cr->port_dst_m);
	iicStmt_->setInt(5, cr->ip_proto_m);	// protocol
	char	firstUpdate[30];
	timevalToDate(&cr->firstUpdate_, firstUpdate);
	iicStmt_->setString(6, firstUpdate);

	char	lastUpdate[30];
	timevalToDate(&cr->lastUpdate_, lastUpdate);
	iicStmt_->setString(7, lastUpdate);	

	iicStmt_->setInt(8, cr->packets_m);	
	iicStmt_->setInt(9, cr->volume_m);
	iicStmt_->setString(10, interfaceName);

	char	flushed_at[30];
	if (now) {
		timevalToDate(now, flushed_at);
		iicStmt_->setString(11, flushed_at);	
	} else {
		iicStmt_->setNull(11, Types::VARCHAR);
	}
	// TODO: binary saved data
	iicStmt_->setNull(11, Types::LONGVARBINARY);
//	pthread_mutex_unlock(&SqlWriteMutex_);
	
	try {
		iicStmt_->executeUpdate();
		return true;
	} catch(SQLException& e) { 
		syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_INFO), "INSERT INTO connections failed %s", e.getMessage().c_str());
		Disconnect();
		return false;
	}
}

int	SQLData::GetDNS(string resolvedName, const struct in_addr& ip, const char *mac)
{

	if (connected_ == false)
		if (!Connect()) 
			return -1;

	int		retCode= 0;
	char	ip_char[inet_to_stringSize];
	inet_to_char(ip, ip_char);

//	q_hostname = resolvedName.c_str();
//	q_ip_address = ip_char;

	sdnsStmt_->setString(1, resolvedName);
	sdnsStmt_->setString(2, ip_char);
	for (;;) {
		auto_ptr<ResultSet> rs;
		try {
			rs = auto_ptr<ResultSet>(sdnsStmt_->executeQuery());
		} catch(SQLException& e) { 
			syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_INFO), "SELECT serial failed %s", e.getMessage().c_str());
			retCode = -1;
			Disconnect();
			break;
		}

		if (!rs->next()) {	// no records, add to db
			pinsertStmt_->setString(1, resolvedName);
			pinsertStmt_->setString(2, ip_char);
			if (mac != NULL)
				pinsertStmt_->setString(3, mac);
			else
				pinsertStmt_->setNull(3, Types::LONGVARCHAR);
			try {
				pinsertStmt_->executeUpdate();
			} catch(SQLException& e) { 
				syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_INFO), "INSERT INTO dnsentry failed %s", e.getMessage().c_str());
				retCode = -1;
				Disconnect();
				break;
			}
		} else {
			rs->getRow();
			retCode = rs->getInt(1);
			break;
		}
	}
//	pthread_mutex_unlock(&SqlWriteMutex_);
	return retCode;
}

